#ifndef _STRAWGUI_MATH_H_
#define _STRAWGUI_MATH_H_

#include "sSaveable.h"
#ifdef _EDITOR
#include "sRTTIObject.h"
#endif

namespace sgui
{
namespace math
{

template < typename T >
class sPoint
{
public:
	T X;
	T Y;
public:
	const T& getX() const { return X; }
	const T& getY() const { return Y; }

	void setX(const T& x) { X = x; }
	void setY(const T& y) { Y = y; }
	void appendX(const T& x) { X += x; }
	void appendY(const T& y) { Y += y; }
	void set(const T& x, const T& y) { X = x; Y = y; }
	void set(const sPoint<T>& other) { X = other.X; Y = other.Y; }

    template < typename TO >
    const sPoint<TO> to() const
    {
        return sPoint<TO>((TO)X, (TO)Y);
    }

public:

    const bool operator == (const sPoint<T>& other) const
    {
        return (X == other.X) && (Y == other.Y);
    }

    const bool operator != (const sPoint<T>& other) const
    {
        return (X != other.X) || (Y != other.Y);
    }

    const sPoint<T> operator += (const sPoint<T>& other)
    {
        X += other.X;
        Y += other.Y;
        return *this;
    }

    const sPoint<T> operator+ (const sPoint<T>& other) const
    {
        sPoint p(*this);
        p += other;
        return p;
    }

    const sPoint<T> operator -= (const sPoint<T>& other)
    {
        X -= other.X;
        Y -= other.Y;
        return *this;
    }

    const sPoint<T> operator- (const sPoint<T>& other) const
    {
        sPoint p(*this);
        p -= other;
        return p;
    }

    const sPoint<T> operator *= (const sPoint<T>& other)
    {
        X *= other.X;
        Y *= other.Y;
        return *this;
    }

    const sPoint<T> operator* (const sPoint<T>& other) const
    {
        sPoint p(*this);
        p *= other;
        return p;
    }

	sPoint(const T& x, const T& y) : X(x), Y(y) {}
	sPoint() : X(0), Y(0) {}
    sPoint(const sPoint& other) : X(other.X), Y(other.Y) {}
};


template < typename T >
class sPointSaveable : public sPoint<T>
	, public sSaveable
#ifdef _EDITOR
	, public rtti::sObject
#endif
{
public:	
	void save(db::Record& file) const
	{
		char data[sizeof(T) * 2];
		memcpy(&data[0], &X, sizeof(T));
		memcpy(&data[sizeof(T)], &Y, sizeof(T));
		file.Set(data, sizeof(T) * 2);
	}

	void load(const db::Record& file)
	{
		if (file.GetDataSize() == sizeof(T) * 2)
		{
			const char* data = (const char*)file.GetAsData();

			memcpy(&X, &data[0], sizeof(T));
			memcpy(&Y, &data[sizeof(T)], sizeof(T));
		}
	}

    void clear()
    {
        X = 0;
        Y = 0;
    }
public:
	
#ifdef _EDITOR
	void exportRTTI(rtti::sInfoList& list)
	{
		REGISTER_RTTI(L"X", TEXT, NEW_RTTI_TEMPLATE_PARAMETER(
			INT, T, sPoint, this, &sPoint::setX, &sPoint::getX
		));
		REGISTER_RTTI(L"Y", TEXT, NEW_RTTI_TEMPLATE_PARAMETER(
			INT, T, sPoint, this, &sPoint::setY, &sPoint::getY
		));
	}
#endif

	sPointSaveable(const T& x, const T& y) : sPoint(x, y) {}
	sPointSaveable() : sPoint() {}
    sPointSaveable(const sPoint& other) : sPoint(other) {}
};

typedef sPoint<float> sPointf;
typedef sPoint<int> sPointi;

typedef sPointSaveable<float> sPointsf;
typedef sPointSaveable<int> sPointsi;

template < typename T >
class sRect
{
public:
	T X;
	T Y;
	T W;
	T H;
public:
	const T& getX() const { return X; }
	const T& getY() const { return Y; }
	const T& getW() const { return W; }
	const T& getH() const { return H; }

	sPoint<T> getPosition() const { return sPoint<T>(X, Y); }
	sPoint<T> getSize() const { return sPoint<T>(W, H); }
	
	void addPosition(const sPoint<T>& offset) { X += offset.X; Y += offset.Y; }
	void addSize(const sPoint<T>& offset) { W += offset.X; H += offset.Y; }

	void setX(const T& x) { X = x; }
	void setY(const T& y) { Y = y; }
	void setW(const T& w) { W = w; }
	void setH(const T& h) { H = h; }

	bool pointIn(const sPoint<T>& point) const { return (point.X >= X && point.Y >= Y && point.X < X + W && point.Y < Y + H); }
	
	void setPosition(const sPoint<T>& position) { X = position.X; Y = position.Y; }
	void setSize(const sPoint<T>& size) { W = size.X; H = size.Y; }
	void set(const T& x, const T& y, const T& w, const T& h) { X = x; Y = y; W = w; H = h; }
	void set(const sRect<T>& from) { X = from.X; Y = from.Y; W = from.W; H = from.H; }

    template < typename TO >
    const sRect<TO> to() const
    {
        return sRect<TO>(X, Y, W, H);
    }

	bool isLegal() const { return W > 0 && H > 0; }

	sRect() : X(0), Y(0), W(0), H(0) {}
	sRect(const T& x, const T& y, const T& w, const T& h) : X(x), Y(y), W(w), H(h) {}
	sRect(const sPoint<T>& position, const sPoint<T>& size) : X(position.X), Y(position.Y), W(size.X), H(size.Y) {}
};


template < typename T>
class sRectSaveable : public sRect<T>
	, public sSaveable
#ifdef _EDITOR
	, public rtti::sObject
#endif
{
public:

	void save(db::Record& file) const
	{
		char data[sizeof(T) * 4];

		memcpy(&data[0],				&X, sizeof(T));
		memcpy(&data[sizeof(T)],		&Y, sizeof(T));
		memcpy(&data[sizeof(T) * 2],	&W, sizeof(T));
		memcpy(&data[sizeof(T) * 3],	&H, sizeof(T));

		file.Set(data, sizeof(T) * 4);
	}

    void clear()
    {
        X = 0;
        Y = 0;
        W = 0;
        H = 0;
    }

	void load(const db::Record& file)
	{
		if (file.GetDataSize() == sizeof(T) * 4)
		{
			const char* data = (const char*)file.GetAsData();

			memcpy(&X, &data[0],				sizeof(T));
			memcpy(&Y, &data[sizeof(T)],		sizeof(T));
			memcpy(&W, &data[sizeof(T) * 2],	sizeof(T));
			memcpy(&H, &data[sizeof(T) * 3],	sizeof(T));

		}
	}
public:
#ifdef _EDITOR
	void exportRTTI(rtti::sInfoList& list)
	{
		
		REGISTER_RTTI(L"Left", TEXT, NEW_RTTI_TEMPLATE_PARAMETER(
			INT, T, sRect, this, &sRect::setX, &sRect::getX
		));
		REGISTER_RTTI(L"Top", TEXT, NEW_RTTI_TEMPLATE_PARAMETER(
			INT, T, sRect, this, &sRect::setY, &sRect::getY
		));
		REGISTER_RTTI(L"Width", TEXT, NEW_RTTI_TEMPLATE_PARAMETER(
			INT, T, sRect, this, &sRect::setW, &sRect::getW
		));
		REGISTER_RTTI(L"Height", TEXT, NEW_RTTI_TEMPLATE_PARAMETER(
			INT, T, sRect, this, &sRect::setH, &sRect::getH
		));
		
	}
#endif

	sRectSaveable() : sRect() {}
	sRectSaveable(const T& x, const T& y, const T& w, const T& h) : sRect(x, y, w, h) {}
	sRectSaveable(const sPoint<T>& position, const sPoint<T>& size) : sRect(position, size) {}

};

typedef sRect<float> sRectf;
typedef sRect<int> sRecti;

typedef sRectSaveable<float> sRectsf;
typedef sRectSaveable<int> sRectsi;

};
};

#endif